#include <cstdio>
#include <cstring>
#include <cctype>

#include "syscomm.h"
#include "utils.h"
#include "dataconf.h"
#define PROBLEMDEF_INCOMPLETE "\nProblem definition is incomplete before end-of-file."

using namespace std;
using namespace fdutils;
using namespace progutils;

char line_buff[GENERAL_LEN];

char dataconf_t::_findnext(FILE* &file){
	if(feof(file)) return 0;
	char tmp=fgetc(file);
	while(!isalnum(tmp) && !ispunct(tmp) && !feof(file))
		tmp=fgetc(file);
	fseek(file,-1,SEEK_CUR);
	if(!isalnum(tmp) && !ispunct(tmp)) return 0;
	return tmp;
}

char dataconf_t::_findbeside(FILE* &file){
	if(feof(file)) return 0;
	char tmp=fgetc(file);
	fseek(file,-1,SEEK_CUR);
	return tmp;
}

bool dataconf_t::_getcontent(FILE* &file, char *target, int targetlen, const char *header, int headerlen, bool endable=false){
	/* !!! Warning: target will be erased first. !!! */
	_findnext(file);
	if(feof(file)) return false;
	fgets(line_buff,GENERAL_LEN,file);

	while(!isprint(line_buff[strlen(line_buff)-1]))
		line_buff[strlen(line_buff)-1]=0;

	if(strcmp(line_buff,"end")==0) return false;

	if(memcmp(line_buff,header,strlen(header)))
		progutils::error("\nA syntax error is found in configuration file.\n");
	memset(target,0,targetlen);
	memcpy(target, line_buff+headerlen, strlen(line_buff)-headerlen);
	return true;
}

bool dataconf_t::getnextprob(const char *prefix){
	static char line_buff[GENERAL_LEN];
	if(!_fdataconf) progutils::error("\nNo data configuration file have been opened.");

	problems.resize(problems.size()+1);

	if(!_getcontent(_fdataconf,problems[problems.size()-1]._title,GENERAL_LEN,"problem",8, true)){
		problems.pop_back();
		return false;
	}

	if(!_getcontent(_fdataconf,problems[problems.size()-1]._ignsettings,GENERAL_LEN,"judge ignore",13))
		error(PROBLEMDEF_INCOMPLETE);

	if(!_getcontent(_fdataconf,problems[problems.size()-1]._infile,PATH_LEN,"input",6))
		error(PROBLEMDEF_INCOMPLETE);

	if(!_getcontent(_fdataconf,problems[problems.size()-1]._outfile,PATH_LEN,"output",7))
		error(PROBLEMDEF_INCOMPLETE);

	/*
	 * The following lines are part of draft standard of data config files.
	 * Now test for an optional checker mode.
	 * In the original standard, the next line in a legal dataconf file should be
	 * test case 1, but in new standard, it may be like this:
	 *   checker <checker_path>
	 * where <checker_path> should be a compiled binary file for unix, path is related to data/.
	 */
	if(!_findnext(_fdataconf)) return true;
	fpos_t bkp;
	fgetpos(_fdataconf,&bkp);
	fgets(line_buff,GENERAL_LEN,_fdataconf);
	if(memcmp(line_buff,"checker",7)==0){
		problems[problems.size()-1]._checkermode=true;
		memcpy(problems[problems.size()-1]._checker,line_buff+8,strlen(line_buff)-8);
		relate(problems[problems.size()-1]._checker,problems[problems.size()-1]._checker,prefix);
	}
	else{
		problems[problems.size()-1]._checkermode=false;
		fsetpos(_fdataconf,&bkp);
	}
	return true;
}

bool dataconf_t::getfile(const char *filepath){
	if(_fdataconf) finish();
	_fdataconf=fopen(filepath,"r");
	return _fdataconf;
}

bool dataconf_t::getnextcase(const char *prefix){
	if(_findnext(_fdataconf)!='c') return false;

	static case_container tmpcase;
	tmpcase.clear();

	_getcontent(_fdataconf,tmpcase._casecode,CASECODE_LEN,"case",5);

	_getcontent(_fdataconf,tmpcase._infile,PATH_LEN,"input",6);
	relate(tmpcase._infile,tmpcase._infile,prefix);

	_getcontent(_fdataconf,tmpcase._outfile,PATH_LEN,"output",7);
	relate(tmpcase._outfile,tmpcase._outfile,prefix);

	_findnext(_fdataconf);
	fscanf(_fdataconf,"timelimit %d",&tmpcase._timelimit);
	_findnext(_fdataconf);
	fscanf(_fdataconf,"memory %d",&tmpcase._memlimit);
	_findnext(_fdataconf);
	fscanf(_fdataconf,"score %d",&tmpcase._fullscore);

	problems[problems.size()-1]._cases.push_back(tmpcase);
	//the vector will make a copy of tmpcase.

	return true;
}
